/*
	brief: step motor drivers
	port map 
		GP419  <-->	 IN4  -> 蓝
		GP420  <-->	 IN3  -> 粉
		GP421  <-->	 IN2  -> 黄
		GP422  <-->	 IN1  -> 橙
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/delay.h>


typedef struct stepmotor_struct
{
    int gpio[4];    // 设备树按照顺序编写
    struct gpio_desc *gpiod[4];
}stepmotor_t;

stepmotor_t *xxh_stepmotor;

static int major = 0;
static struct class *stepmotor_class;

static int step_motor_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
    int len,err;
    int index;
    char step_data;
    
    
    err = copy_from_user(&step_data, buf, size);
    
    //printk("%s line %d: user data <0x%02x>\n", __FUNCTION__, __LINE__, step_data);
    for(index=0; index<4; index++)
    {
        if(step_data&0x1)
            gpio_set_value(xxh_stepmotor->gpio[index], 1);
            //gpiod_set_value(xxh_stepmotor->gpiod[index], 1);
        else
            gpio_set_value(xxh_stepmotor->gpio[index], 0);
            //gpiod_set_value(xxh_stepmotor->gpiod[index], 0);
		step_data >>= 1;
        udelay(100);    // 稳定输出
    }

    return size;
}

static struct file_operations step_motor_drv_ops = {
    .owner = THIS_MODULE,
	.write = step_motor_write,
};


static int xxh_step_motor_probe(struct platform_device *pdev)
{
    int index = 0;
    int count = 0;
    struct device_node *np;
    
    printk("%s line %d\n", __FUNCTION__, __LINE__);

    np = pdev->dev.of_node;
    /* 1 分配驱动专用结构体空间 */
    xxh_stepmotor = kzalloc(sizeof(stepmotor_t), GFP_KERNEL);
    if(xxh_stepmotor == NULL)
        return -1;

    /* 2 获取gpio描述符 */
    #if 1 // 按照gpios顺序获取
    count = of_gpio_count(np);
    for(index = 0; index < count; index++)
    {
        xxh_stepmotor->gpio[index] = of_get_gpio(np, index);
        printk("%s line %d: get gpio number <%d> \n", __FUNCTION__, __LINE__, xxh_stepmotor->gpio[index]);
        xxh_stepmotor->gpiod[index] = gpio_to_desc(xxh_stepmotor->gpio[index]);
        gpiod_direction_output(xxh_stepmotor->gpiod[index], 0);
    }
    #else // 按照标识符获取
     
    #endif
    /* 3 注册file_operations结构体 */
    major = register_chrdev(major, "xxh_step_motor", &step_motor_drv_ops);
    stepmotor_class = class_create(THIS_MODULE, "xxh_stepmotor");
    if(IS_ERR(stepmotor_class)) {
        pr_err("stepmotor_class is error\n");
        unregister_chrdev(major, "xxh_step_motor");
        return -1;
    }

    device_create(stepmotor_class, NULL, MKDEV(major, 0),NULL, "xxh_stepmotor");
    return 0;
}

static int xxh_step_motor_remove(struct platform_device *pdev)
{
    int count,index;
    struct device_node *np;
    
    printk("%s line %d\n", __FUNCTION__, __LINE__);
    
    np = pdev->dev.of_node;
    count = of_gpio_count(np);

    device_destroy(stepmotor_class, MKDEV(major, 0));
    class_destroy(stepmotor_class);
    unregister_chrdev(major, "xxh_step_motor");

    for(index=0; index<count; index++)
    {
        gpiod_put(xxh_stepmotor->gpiod[index]);
    }

    kfree(xxh_stepmotor);
}

static const struct of_device_id step_motor_match[] = {
	{.compatible = "xxh,stepmotor-drv"},
	{},
};

static struct platform_driver xxh_step_motor_driver = {
	.probe = xxh_step_motor_probe,
	.remove = xxh_step_motor_remove,
	.driver = {
		.name = "xxh,stepmotor",
		.of_match_table = step_motor_match,
	}
};


static int __init step_motor_init(void)
{
	int err;
    
    printk("%s line %d\n", __FUNCTION__, __LINE__);
	err = platform_driver_register(&xxh_step_motor_driver);
    return err;
}

static void __exit step_motor_exit(void)
{
    printk("%s line %d\n", __FUNCTION__, __LINE__);
    
	platform_driver_unregister(&xxh_step_motor_driver);
	return;
}


module_init(step_motor_init);
module_exit(step_motor_exit);

MODULE_AUTHOR("xxh<1293793140@qq.com>");
MODULE_DESCRIPTION("support step motor 28BYJ-48");
MODULE_LICENSE("GPL");
